package com.abetsy.vben.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.abetsy.vben.dao.DramaShareMapper;
import com.abetsy.vben.dao.GlobalConfigMapper;
import com.abetsy.vben.dao.ProxyImageMapper;
import com.abetsy.vben.entity.DramaShare;
import com.abetsy.vben.entity.ProxyImage;
import com.abetsy.vben.entity.SysUser;
import com.abetsy.vben.entity.util.ProxyInfo;
import com.abetsy.vben.param.DramaSharePreviewParam;
import com.abetsy.vben.param.ShareDramaListParam;
import com.abetsy.vben.param.ShareDramaParam;
import com.abetsy.vben.param.SharePreviewParam;
import com.abetsy.vben.service.PreviewStrategy;
import com.abetsy.vben.utils.FnosUtil;
import com.abetsy.vben.vo.DramaDetailsVo;
import com.abetsy.vben.vo.DramaVo;
import com.abetsy.vben.entity.GlobalConfig;
import com.abetsy.vben.entity.constant.TMDBConstant;
import com.abetsy.vben.service.TMDBService;
import com.abetsy.vben.utils.i18n.VbenException;
import com.abetsy.vben.vo.SeasonVo;
import com.abetsy.vben.vo.ShareDramaInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class TMDBServiceImpl implements TMDBService {

    @Resource
    private GlobalConfigMapper globalConfigMapper;

    @Resource
    private ProxyImageMapper proxyImageMapper;

    @Resource
    private DramaShareMapper dramaShareMapper;

    @Resource
    private PluginRegistry<PreviewStrategy, DramaSharePreviewParam> registry;

    /**
     * 获取代理信息
     */
    @Override
    public ProxyInfo proxyInfo() {
        int userId = Integer.parseInt(StpUtil.getLoginId().toString());
        return proxyInfo(userId);
    }

    /**
     * 查询代理状态
     */
    @Override
    public ProxyInfo getProxyInfo() {
        ProxyInfo proxyInfo = new ProxyInfo();
        QueryWrapper<GlobalConfig> configQueryWrapper = new QueryWrapper<>();
        configQueryWrapper.eq("user_id", Integer.parseInt(StpUtil.getLoginId().toString()));
        GlobalConfig globalConfig = globalConfigMapper.selectOne(configQueryWrapper);
        if (globalConfig == null) {
            proxyInfo.setProxyStatus(false);
        } else {
            proxyInfo.setProxyStatus(true);
            proxyInfo.setProxyIp(globalConfig.getProxyIp());
            proxyInfo.setProxyPort(globalConfig.getProxyPort());
        }
        return proxyInfo;
    }

    /**
     * 获取代理信息(静态资源适配)
     */
    @Override
    public ProxyInfo proxyInfo(Integer userId) {
        ProxyInfo proxyInfo = new ProxyInfo();
        QueryWrapper<GlobalConfig> configQueryWrapper = new QueryWrapper<>();
        configQueryWrapper.eq("user_id", userId);
        GlobalConfig globalConfig = globalConfigMapper.selectOne(configQueryWrapper);
        if (globalConfig == null) {
            proxyInfo.setProxyStatus(false);
            return proxyInfo;
        } else {
            try {
                HttpRequest.get(TMDBConstant.BASE_URL).setHttpProxy(globalConfig.getProxyIp(), globalConfig.getProxyPort()).setConnectionTimeout(2000).execute();
                proxyInfo.setProxyStatus(true);
                proxyInfo.setProxyIp(globalConfig.getProxyIp());
                proxyInfo.setProxyPort(globalConfig.getProxyPort());
                return proxyInfo;
            } catch (Exception e) {
                proxyInfo.setProxyStatus(false);
                return proxyInfo;
            }
        }
    }

    @Override
    public ResponseEntity<byte[]> proxyImage(String imagePath) {
        try {
            // 先检查是否有缓存
            QueryWrapper<ProxyImage> imageQueryWrapper = new QueryWrapper<>();
            imageQueryWrapper.eq("image_path", imagePath);
            ProxyImage proxyImage = proxyImageMapper.selectOne(imageQueryWrapper);
            byte[] data;
            if (proxyImage == null) {
                HttpRequest httpRequest = HttpRequest.get(String.format("%s/v/api/v1/sys/rimg/%s", FnosUtil.getInstance().BaseUrl(), imagePath));
                try (HttpResponse execute = httpRequest.execute()) {
                    data = execute.bodyBytes();
                }
                ProxyImage proxyImageInfo = new ProxyImage();
                proxyImageInfo.setImagePath(imagePath);
                proxyImageInfo.setImageMd5(SecureUtil.md5(imagePath));
                proxyImageInfo.setImageByte(data);
                proxyImageMapper.insert(proxyImageInfo);
            } else {
                data = proxyImage.getImageByte();
            }
            HttpHeaders headers = new HttpHeaders();
            if (StrUtil.endWithAnyIgnoreCase(imagePath, ".jpg", ".jpeg")) {
                headers.setContentType(MediaType.IMAGE_JPEG);
            } else if (StrUtil.endWithIgnoreCase(imagePath, ".png")) {
                headers.setContentType(MediaType.IMAGE_PNG);
            } else if (StrUtil.endWithIgnoreCase(imagePath, ".gif")) {
                headers.setContentType(MediaType.IMAGE_GIF);
            } else if (StrUtil.endWithIgnoreCase(imagePath, ".webp")) {
                headers.add(HttpHeaders.CONTENT_TYPE, "image/webp");
            }
            return new ResponseEntity<>(data, headers, HttpStatus.OK);
        } catch (Exception e) {
            throw new VbenException("proxy.image.error", new Object[]{}, LocaleContextHolder.getLocale());
        }
    }

    @Override
    public List<DramaVo> searchDrama(String dramaName, HttpServletRequest request) {
        return FnosUtil.getInstance().searchDrama(dramaName, request);
    }

    @Override
    public DramaDetailsVo dramaDetails(Integer id, HttpServletRequest request) {
        DramaDetailsVo dramaDetailsVo = new DramaDetailsVo();
        try {
            String url = String.format(TMDBConstant.DRAMA_DETAILS_KEY, id, LocaleContextHolder.getLocale().getLanguage());
            HttpRequest httpRequest = HttpRequest.get(url);
            ProxyInfo proxyInfo = getProxyInfo();
            if (proxyInfo.isProxyStatus()) {
                httpRequest.setHttpProxy(proxyInfo.getProxyIp(), proxyInfo.getProxyPort());
            }
            HttpResponse execute = httpRequest.execute();
            JSONObject parseObj = JSONUtil.parseObj(execute.body());
            dramaDetailsVo.setName(parseObj.getStr("name"));
            dramaDetailsVo.setOverview(parseObj.getStr("overview"));
            JSONArray jsonArray = JSONUtil.parseArray(parseObj.getByPath("seasons"));
            List<SeasonVo> seasonVoList = new ArrayList<>();
            for (Object object : jsonArray) {
                JSONObject item = JSONUtil.parseObj(object);
                if (item.getInt("episode_count") > 0) {
                    SeasonVo seasonVo = new SeasonVo();
                    seasonVo.setTime(item.getStr("air_date"));
                    seasonVo.setCount(item.getInt("episode_count"));
                    seasonVo.setSid(item.getInt("id"));
                    seasonVo.setTitle(item.getStr("name"));
                    String basePoster = item.getStr("poster_path");
                    seasonVo.setSnumber(item.getInt("season_number"));
                    seasonVo.setAverage(item.getDouble("vote_average"));
                    if (StrUtil.isNotBlank(basePoster)) {
                        if (proxyInfo.isProxyStatus()) {
                            int userId = Integer.parseInt(StpUtil.getLoginId().toString());
                            String baseUrl = String.format("%s://%s/api/tmdb/image/%d%s", request.getScheme(), request.getHeader("host"), userId, basePoster);
                            seasonVo.setImage(baseUrl);
                        } else {
                            seasonVo.setImage(TMDBConstant.BASE_IMAGES + basePoster);
                        }
                    }
                    seasonVo.setSource(String.format(TMDBConstant.BASE_DRAMA_SEASON_UI, id, seasonVo.getSnumber()));
                    seasonVoList.add(seasonVo);
                }
            }
            dramaDetailsVo.setSeasonVoList(seasonVoList);
            return dramaDetailsVo;
        } catch (Exception e) {
            return dramaDetailsVo;
        }
    }

    @Override
    public ShareDramaInfoVo dramaPreview(DramaSharePreviewParam param) {
        PreviewStrategy previewStrategy = registry.getRequiredPluginFor(param);
        return previewStrategy.previewDramaShare(param);
    }

    @Override
    public Object dramaShare(ShareDramaParam param) {
        DramaShare dramaShare = new DramaShare();
        dramaShare.setUserId(Integer.parseInt(StpUtil.getLoginId().toString()));
        BeanUtil.copyProperties(param, dramaShare);
        dramaShare.setStatus("ok");
        dramaShareMapper.insert(dramaShare);
        return null;
    }

    @Override
    public Object dramaList(ShareDramaListParam param) {
        Page<Map<String, Object>> page = new Page<>(param.getPageNum(), param.getPageSize());
        MPJLambdaWrapper<DramaShare> children = JoinWrappers.lambda(DramaShare.class).selectAll(DramaShare.class)
                .select(SysUser::getNickname).leftJoin(SysUser.class, SysUser::getUid, DramaShare::getUserId)
                .eq(DramaShare::getDramaId, param.getDramaId()).eq(DramaShare::getSeasonId, param.getSeasonId());
        if (!StrUtil.equals(param.getCloudType(), "no")) {
            children.eq(DramaShare::getCloudType, param.getCloudType());
        }
        if (!StrUtil.equals(param.getVideoResolution(), "no")) {
            children.eq(DramaShare::getVideoResolution, param.getVideoResolution());
        }
        if (!StrUtil.equals(param.getVideoType(), "no")) {
            children.eq(DramaShare::getVideoType, param.getVideoType());
        }
        children.orderByDesc(DramaShare::getLastCount, DramaShare::getCreateTime);
        IPage<Map<String, Object>> dramaPage = dramaShareMapper.selectJoinMapsPage(page, children);
        dramaPage.getRecords().forEach(x -> {
            x.put("meShare", StrUtil.equals(x.get("user_id").toString(), StpUtil.getLoginId().toString()));
        });
        Map<String, Object> data = new HashMap<>();
        data.put("total", dramaPage.getTotal());
        data.put("pageNum", param.getPageNum());
        data.put("pageSize", param.getPageSize());
        data.put("data", dramaPage.getRecords());
        return data;
    }

    @Override
    public ShareDramaInfoVo sharePreview(SharePreviewParam param) {
        DramaShare dramaShare = dramaShareMapper.selectById(param.getId());
        if (dramaShare == null) {
            VbenException.locale("season.resource.empty");
        }
        DramaSharePreviewParam dramaSharePreviewParam = new DramaSharePreviewParam();
        BeanUtil.copyProperties(dramaShare, dramaSharePreviewParam, true);
        PreviewStrategy previewStrategy = registry.getRequiredPluginFor(dramaSharePreviewParam);
        ShareDramaInfoVo shareDramaInfoVo = previewStrategy.previewDramaShare(dramaSharePreviewParam);
        assert dramaShare != null;
        dramaShare.setLastCount(shareDramaInfoVo.getLastCount());
        dramaShare.setLastName(shareDramaInfoVo.getLastName());
        if (shareDramaInfoVo.isStatus()) {
            dramaShare.setStatus("ok");
            dramaShare.setMsg("");
        }else {
            dramaShare.setStatus("error");
            dramaShare.setMsg(shareDramaInfoVo.getMsg());
        }
        dramaShareMapper.updateById(dramaShare);
        return shareDramaInfoVo;
    }

    @Override
    public Object shareDelete(SharePreviewParam param) {
        DramaShare dramaShare = dramaShareMapper.selectById(param.getId());
        if (dramaShare == null) {
            VbenException.locale("season.resource.empty");
        }
        int userId = Integer.parseInt(StpUtil.getLoginId().toString());
        assert dramaShare != null;
        if (userId == dramaShare.getUserId()) {
            dramaShareMapper.deleteById(param.getId());
        }else {
            VbenException.locale("season.resource.delete.share.user");
        }
        return "OK";
    }

}
